{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercise 3 - Solution"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we want to use Marshmallow to do the serialization and deserialization that we did in Exercises 1 and 2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Stock:\n",
    "    def __init__(self, symbol, date, open_, high, low, close, volume):\n",
    "        self.symbol = symbol\n",
    "        self.date = date\n",
    "        self.open = open_\n",
    "        self.high = high\n",
    "        self.low = low\n",
    "        self.close = close\n",
    "        self.volume = volume\n",
    "        \n",
    "class Trade:\n",
    "    def __init__(self, symbol, timestamp, order, price, volume, commission):\n",
    "        self.symbol = symbol\n",
    "        self.timestamp = timestamp\n",
    "        self.order = order\n",
    "        self.price = price\n",
    "        self.commission = commission\n",
    "        self.volume = volume"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import date, datetime\n",
    "from decimal import Decimal\n",
    "\n",
    "activity = {\n",
    "    \"quotes\": [\n",
    "        Stock('TSLA', date(2018, 11, 22), \n",
    "              Decimal('338.19'), Decimal('338.64'), Decimal('337.60'), Decimal('338.19'), 365_607),\n",
    "        Stock('AAPL', date(2018, 11, 22), \n",
    "              Decimal('176.66'), Decimal('177.25'), Decimal('176.64'), Decimal('176.78'), 3_699_184),\n",
    "        Stock('MSFT', date(2018, 11, 22), \n",
    "              Decimal('103.25'), Decimal('103.48'), Decimal('103.07'), Decimal('103.11'), 4_493_689)\n",
    "    ],\n",
    "    \n",
    "    \"trades\": [\n",
    "        Trade('TSLA', datetime(2018, 11, 22, 10, 5, 12), 'buy', Decimal('338.25'), 100, Decimal('9.99')),\n",
    "        Trade('AAPL', datetime(2018, 11, 22, 10, 30, 5), 'sell', Decimal('177.01'), 20, Decimal('9.99'))\n",
    "    ]\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I'm first going to define some schemas for trades and stocks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from marshmallow import Schema, fields"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StockSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    date = fields.Date()\n",
    "    open = fields.Decimal()\n",
    "    high = fields.Decimal()\n",
    "    low = fields.Decimal()\n",
    "    close = fields.Decimal()\n",
    "    volume = fields.Integer()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's test this one out quickly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "MarshalResult(data={'low': Decimal('337.60'), 'open': Decimal('338.19'), 'close': Decimal('338.19'), 'volume': 365607, 'symbol': 'TSLA', 'high': Decimal('338.64'), 'date': '2018-11-22'}, errors={})"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "StockSchema().dump(Stock('TSLA', date(2018, 11, 22), \n",
    "                          Decimal('338.19'), Decimal('338.64'), Decimal('337.60'), \n",
    "                          Decimal('338.19'), 365_607))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's great, but there's a slight issue - you'll notice that the marshalled data has `Decimal` objects for our prices. This is still going to be an issue if we try to serialize to JSON:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "Object of type 'Decimal' is not JSON serializable",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-6-fff3c2bd8bbe>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m StockSchema().dumps(Stock('TSLA', date(2018, 11, 22), \n\u001b[1;32m      2\u001b[0m                           \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'338.19'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'338.64'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mDecimal\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'337.60'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m                           Decimal('338.19'), 365_607))\n\u001b[0m",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36mdumps\u001b[0;34m(self, obj, many, update_fields, *args, **kwargs)\u001b[0m\n\u001b[1;32m    551\u001b[0m         \"\"\"\n\u001b[1;32m    552\u001b[0m         \u001b[0mdeserialized\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdump\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmany\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmany\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mupdate_fields\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mupdate_fields\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 553\u001b[0;31m         \u001b[0mret\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson_module\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdeserialized\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    554\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mMarshalResult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mret\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    555\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/json/__init__.py\u001b[0m in \u001b[0;36mdumps\u001b[0;34m(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)\u001b[0m\n\u001b[1;32m    229\u001b[0m         \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mindent\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mseparators\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;32mand\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    230\u001b[0m         default is None and not sort_keys and not kw):\n\u001b[0;32m--> 231\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0m_default_encoder\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    232\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mcls\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    233\u001b[0m         \u001b[0mcls\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mJSONEncoder\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/json/encoder.py\u001b[0m in \u001b[0;36mencode\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m    197\u001b[0m         \u001b[0;31m# exceptions aren't as detailed.  The list call should be roughly\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    198\u001b[0m         \u001b[0;31m# equivalent to the PySequence_Fast that ''.join() would do.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 199\u001b[0;31m         \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_one_shot\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    200\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mlist\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtuple\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    201\u001b[0m             \u001b[0mchunks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mchunks\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/json/encoder.py\u001b[0m in \u001b[0;36miterencode\u001b[0;34m(self, o, _one_shot)\u001b[0m\n\u001b[1;32m    255\u001b[0m                 \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkey_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mitem_separator\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msort_keys\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    256\u001b[0m                 self.skipkeys, _one_shot)\n\u001b[0;32m--> 257\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0m_iterencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mo\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    258\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    259\u001b[0m def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/json/encoder.py\u001b[0m in \u001b[0;36mdefault\u001b[0;34m(self, o)\u001b[0m\n\u001b[1;32m    178\u001b[0m         \"\"\"\n\u001b[1;32m    179\u001b[0m         raise TypeError(\"Object of type '%s' is not JSON serializable\" %\n\u001b[0;32m--> 180\u001b[0;31m                         o.__class__.__name__)\n\u001b[0m\u001b[1;32m    181\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    182\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mo\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mTypeError\u001b[0m: Object of type 'Decimal' is not JSON serializable"
     ]
    }
   ],
   "source": [
    "StockSchema().dumps(Stock('TSLA', date(2018, 11, 22), \n",
    "                          Decimal('338.19'), Decimal('338.64'), Decimal('337.60'), \n",
    "                          Decimal('338.19'), 365_607))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So let's fix that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StockSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    date = fields.Date()\n",
    "    open = fields.Decimal(as_string=True)\n",
    "    high = fields.Decimal(as_string=True)\n",
    "    low = fields.Decimal(as_string=True)\n",
    "    close = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'low': '337.60',\n",
       " 'open': '338.19',\n",
       " 'close': '338.19',\n",
       " 'volume': 365607,\n",
       " 'symbol': 'TSLA',\n",
       " 'high': '338.64',\n",
       " 'date': '2018-11-22'}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "StockSchema().dump(Stock('TSLA', date(2018, 11, 22), \n",
    "                          Decimal('338.19'), Decimal('338.64'), Decimal('337.60'), \n",
    "                          Decimal('338.19'), 365_607)).data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we can serialize to JSON:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{\"low\": \"337.60\", \"open\": \"338.19\", \"close\": \"338.19\", \"volume\": 365607, \"symbol\": \"TSLA\", \"high\": \"338.64\", \"date\": \"2018-11-22\"}'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "StockSchema().dumps(Stock('TSLA', date(2018, 11, 22), \n",
    "                          Decimal('338.19'), Decimal('338.64'), Decimal('337.60'), \n",
    "                          Decimal('338.19'), 365_607)).data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now handle the `Trade` schema:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TradeSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    timestamp = fields.DateTime()\n",
    "    order = fields.Str()\n",
    "    price = fields.Decimal(as_string=True)\n",
    "    commission = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'{\"price\": \"338.25\", \"volume\": 100, \"symbol\": \"TSLA\", \"order\": \"buy\", \"commission\": \"9.99\", \"timestamp\": \"2018-11-22T10:05:12+00:00\"}'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "TradeSchema().dumps(Trade('TSLA', datetime(2018, 11, 22, 10, 5, 12), 'buy', Decimal('338.25'), 100, Decimal('9.99'))).data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's write a schema for our overall dictionary that contains a list of Trades and a list of Quotes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ActivitySchema(Schema):\n",
    "    trades = fields.Nested(TradeSchema, many=True)\n",
    "    quotes = fields.Nested(StockSchema, many=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can now serialize and deserialize:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "result = ActivitySchema().dumps(activity, indent=2).data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "str"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(result)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"trades\": [\n",
      "    {\n",
      "      \"price\": \"338.25\",\n",
      "      \"volume\": 100,\n",
      "      \"symbol\": \"TSLA\",\n",
      "      \"order\": \"buy\",\n",
      "      \"commission\": \"9.99\",\n",
      "      \"timestamp\": \"2018-11-22T10:05:12+00:00\"\n",
      "    },\n",
      "    {\n",
      "      \"price\": \"177.01\",\n",
      "      \"volume\": 20,\n",
      "      \"symbol\": \"AAPL\",\n",
      "      \"order\": \"sell\",\n",
      "      \"commission\": \"9.99\",\n",
      "      \"timestamp\": \"2018-11-22T10:30:05+00:00\"\n",
      "    }\n",
      "  ],\n",
      "  \"quotes\": [\n",
      "    {\n",
      "      \"low\": \"337.60\",\n",
      "      \"open\": \"338.19\",\n",
      "      \"close\": \"338.19\",\n",
      "      \"volume\": 365607,\n",
      "      \"symbol\": \"TSLA\",\n",
      "      \"high\": \"338.64\",\n",
      "      \"date\": \"2018-11-22\"\n",
      "    },\n",
      "    {\n",
      "      \"low\": \"176.64\",\n",
      "      \"open\": \"176.66\",\n",
      "      \"close\": \"176.78\",\n",
      "      \"volume\": 3699184,\n",
      "      \"symbol\": \"AAPL\",\n",
      "      \"high\": \"177.25\",\n",
      "      \"date\": \"2018-11-22\"\n",
      "    },\n",
      "    {\n",
      "      \"low\": \"103.07\",\n",
      "      \"open\": \"103.25\",\n",
      "      \"close\": \"103.11\",\n",
      "      \"volume\": 4493689,\n",
      "      \"symbol\": \"MSFT\",\n",
      "      \"high\": \"103.48\",\n",
      "      \"date\": \"2018-11-22\"\n",
      "    }\n",
      "  ]\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So a JSON string...\n",
    "Let's deserialize that JSON string:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "activity_deser = ActivitySchema().loads(result).data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(activity_deser)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'quotes': [{'close': Decimal('338.19'),\n",
      "             'date': datetime.date(2018, 11, 22),\n",
      "             'high': Decimal('338.64'),\n",
      "             'low': Decimal('337.60'),\n",
      "             'open': Decimal('338.19'),\n",
      "             'symbol': 'TSLA',\n",
      "             'volume': 365607},\n",
      "            {'close': Decimal('176.78'),\n",
      "             'date': datetime.date(2018, 11, 22),\n",
      "             'high': Decimal('177.25'),\n",
      "             'low': Decimal('176.64'),\n",
      "             'open': Decimal('176.66'),\n",
      "             'symbol': 'AAPL',\n",
      "             'volume': 3699184},\n",
      "            {'close': Decimal('103.11'),\n",
      "             'date': datetime.date(2018, 11, 22),\n",
      "             'high': Decimal('103.48'),\n",
      "             'low': Decimal('103.07'),\n",
      "             'open': Decimal('103.25'),\n",
      "             'symbol': 'MSFT',\n",
      "             'volume': 4493689}],\n",
      " 'trades': [{'commission': Decimal('9.99'),\n",
      "             'order': 'buy',\n",
      "             'price': Decimal('338.25'),\n",
      "             'symbol': 'TSLA',\n",
      "             'timestamp': datetime.datetime(2018, 11, 22, 10, 5, 12, tzinfo=tzutc()),\n",
      "             'volume': 100},\n",
      "            {'commission': Decimal('9.99'),\n",
      "             'order': 'sell',\n",
      "             'price': Decimal('177.01'),\n",
      "             'symbol': 'AAPL',\n",
      "             'timestamp': datetime.datetime(2018, 11, 22, 10, 30, 5, tzinfo=tzutc()),\n",
      "             'volume': 20}]}\n"
     ]
    }
   ],
   "source": [
    "from pprint import pprint\n",
    "\n",
    "pprint(activity_deser)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's looking pretty good, but you'll notice something - the objects in the `trades` and `quotes` list have been loaded into plain dictionary objects, not `Trade` and `Stock` objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(activity_deser['trades'][0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For this we have to remember to provide functions decorated with `@post_load`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "from marshmallow import post_load\n",
    "\n",
    "class TradeSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    timestamp = fields.DateTime()\n",
    "    order = fields.Str()\n",
    "    price = fields.Decimal(as_string=True)\n",
    "    commission = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()\n",
    "    \n",
    "    @post_load\n",
    "    def make_trade(self, data):\n",
    "        return Trade(**data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StockSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    date = fields.Date()\n",
    "    open = fields.Decimal(as_string=True)\n",
    "    high = fields.Decimal(as_string=True)\n",
    "    low = fields.Decimal(as_string=True)\n",
    "    close = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()\n",
    "    \n",
    "    @post_load()\n",
    "    def make_stock(self, data):\n",
    "        return Stock(**data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And of course we have to redefine our `ActivitySchema` to make sure it is referencing the newly defined sub schema classes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ActivitySchema(Schema):\n",
    "    trades = fields.Nested(TradeSchema, many=True)\n",
    "    quotes = fields.Nested(StockSchema, many=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now we can try this again:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "__init__() got an unexpected keyword argument 'open'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-23-94f20a210573>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mactivity_deser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mActivitySchema\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36mloads\u001b[0;34m(self, json_data, many, *args, **kwargs)\u001b[0m\n\u001b[1;32m    592\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    593\u001b[0m         \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjson_module\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mloads\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mjson_data\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 594\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmany\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mmany\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpartial\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpartial\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    595\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    596\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mvalidate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmany\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpartial\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(self, data, many, partial)\u001b[0m\n\u001b[1;32m    569\u001b[0m         \u001b[0;34m.\u001b[0m\u001b[0;34m.\u001b[0m \u001b[0mversionadded\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m1.0\u001b[0m\u001b[0;36m.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    570\u001b[0m         \"\"\"\n\u001b[0;32m--> 571\u001b[0;31m         \u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmany\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpartial\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpartial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpostprocess\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    572\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mUnmarshalResult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    573\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36m_do_load\u001b[0;34m(self, data, many, partial, postprocess)\u001b[0m\n\u001b[1;32m    651\u001b[0m                     \u001b[0mpartial\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpartial\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    652\u001b[0m                     \u001b[0mdict_class\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdict_class\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 653\u001b[0;31m                     \u001b[0mindex_errors\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopts\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex_errors\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    654\u001b[0m                 )\n\u001b[1;32m    655\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mValidationError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merror\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/marshalling.py\u001b[0m in \u001b[0;36mdeserialize\u001b[0;34m(self, data, fields_dict, many, partial, dict_class, index_errors, index)\u001b[0m\n\u001b[1;32m    288\u001b[0m                     \u001b[0mfield_name\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfield_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    289\u001b[0m                     \u001b[0mfield_obj\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mfield_obj\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 290\u001b[0;31m                     \u001b[0mindex\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mindex\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mindex_errors\u001b[0m \u001b[0;32melse\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    291\u001b[0m                 )\n\u001b[1;32m    292\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mmissing\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/marshalling.py\u001b[0m in \u001b[0;36mcall_and_store\u001b[0;34m(self, getter_func, data, field_name, field_obj, index)\u001b[0m\n\u001b[1;32m     60\u001b[0m         \"\"\"\n\u001b[1;32m     61\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 62\u001b[0;31m             \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgetter_func\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     63\u001b[0m         \u001b[0;32mexcept\u001b[0m \u001b[0mValidationError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m  \u001b[0;31m# Store validation errors\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     64\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0merror_kwargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/marshalling.py\u001b[0m in \u001b[0;36m<lambda>\u001b[0;34m(val)\u001b[0m\n\u001b[1;32m    281\u001b[0m                     \u001b[0mval\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    282\u001b[0m                     \u001b[0mfield_obj\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload_from\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mattr_name\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 283\u001b[0;31m                     \u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    284\u001b[0m                 )\n\u001b[1;32m    285\u001b[0m                 value = self.call_and_store(\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/fields.py\u001b[0m in \u001b[0;36mdeserialize\u001b[0;34m(self, value, attr, data)\u001b[0m\n\u001b[1;32m    262\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mgetattr\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'allow_none'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mTrue\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mvalue\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    263\u001b[0m             \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 264\u001b[0;31m         \u001b[0moutput\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_deserialize\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mattr\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    265\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_validate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    266\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0moutput\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/fields.py\u001b[0m in \u001b[0;36m_deserialize\u001b[0;34m(self, value, attr, data)\u001b[0m\n\u001b[1;32m    463\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfail\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'type'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minput\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtype\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__name__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    464\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 465\u001b[0;31m         \u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mschema\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    466\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    467\u001b[0m             \u001b[0;32mraise\u001b[0m \u001b[0mValidationError\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(self, data, many, partial)\u001b[0m\n\u001b[1;32m    569\u001b[0m         \u001b[0;34m.\u001b[0m\u001b[0;34m.\u001b[0m \u001b[0mversionadded\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m1.0\u001b[0m\u001b[0;36m.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    570\u001b[0m         \"\"\"\n\u001b[0;32m--> 571\u001b[0;31m         \u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmany\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpartial\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpartial\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpostprocess\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    572\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mUnmarshalResult\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0merrors\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    573\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36m_do_load\u001b[0;34m(self, data, many, partial, postprocess)\u001b[0m\n\u001b[1;32m    676\u001b[0m                     \u001b[0mresult\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    677\u001b[0m                     \u001b[0mmany\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 678\u001b[0;31m                     original_data=data)\n\u001b[0m\u001b[1;32m    679\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mValidationError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    680\u001b[0m                 \u001b[0merrors\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0merr\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnormalized_messages\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36m_invoke_load_processors\u001b[0;34m(self, tag_name, data, many, original_data)\u001b[0m\n\u001b[1;32m    856\u001b[0m             data=data, many=many, original_data=original_data)\n\u001b[1;32m    857\u001b[0m         data = self._invoke_processors(tag_name, pass_many=False,\n\u001b[0;32m--> 858\u001b[0;31m             data=data, many=many, original_data=original_data)\n\u001b[0m\u001b[1;32m    859\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    860\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36m_invoke_processors\u001b[0;34m(self, tag_name, pass_many, data, many, original_data)\u001b[0m\n\u001b[1;32m    954\u001b[0m                             for item in data]\n\u001b[1;32m    955\u001b[0m                 \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 956\u001b[0;31m                     \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mif_none\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprocessor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mitem\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    957\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    958\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0mpass_original\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/marshmallow/schema.py\u001b[0m in \u001b[0;36m<listcomp>\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m    954\u001b[0m                             for item in data]\n\u001b[1;32m    955\u001b[0m                 \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 956\u001b[0;31m                     \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mutils\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mif_none\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mprocessor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitem\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mitem\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    957\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    958\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0mpass_original\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m<ipython-input-21-d0d4a8dc286c>\u001b[0m in \u001b[0;36mmake_stock\u001b[0;34m(self, data)\u001b[0m\n\u001b[1;32m     10\u001b[0m     \u001b[0;34m@\u001b[0m\u001b[0mpost_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     11\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mmake_stock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 12\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mStock\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: __init__() got an unexpected keyword argument 'open'"
     ]
    }
   ],
   "source": [
    "activity_deser = ActivitySchema().loads(result).data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So here we have an issue - basically our method to construct a new `Stock` object expects the argument for the open price to be `open_`, and not `open` which is what our schema is producing.\n",
    "\n",
    "We could do it in one of two ways:\n",
    "\n",
    "First we can change our method that builds the `Stock` object:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StockSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    date = fields.Date()\n",
    "    open = fields.Decimal(as_string=True)\n",
    "    high = fields.Decimal(as_string=True)\n",
    "    low = fields.Decimal(as_string=True)\n",
    "    close = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()\n",
    "    \n",
    "    @post_load()\n",
    "    def make_stock(self, data):\n",
    "        data['open_'] = data.pop('open')\n",
    "        return Stock(**data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ActivitySchema(Schema):\n",
    "    trades = fields.Nested(TradeSchema, many=True)\n",
    "    quotes = fields.Nested(StockSchema, many=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "activity_deser = ActivitySchema().loads(result).data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'quotes': [<__main__.Stock object at 0x105e70e80>,\n",
      "            <__main__.Stock object at 0x105e70eb8>,\n",
      "            <__main__.Stock object at 0x105e70ef0>],\n",
      " 'trades': [<__main__.Trade object at 0x105e70c18>,\n",
      "            <__main__.Trade object at 0x105e70b70>]}\n"
     ]
    }
   ],
   "source": [
    "pprint(activity_deser)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, let's just recap the various schemas we have to create:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "class StockSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    date = fields.Date()\n",
    "    open = fields.Decimal(as_string=True)\n",
    "    high = fields.Decimal(as_string=True)\n",
    "    low = fields.Decimal(as_string=True)\n",
    "    close = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()\n",
    "    \n",
    "    @post_load()\n",
    "    def make_stock(self, data):\n",
    "        data['open_'] = data.pop('open')\n",
    "        return Stock(**data)\n",
    "    \n",
    "class TradeSchema(Schema):\n",
    "    symbol = fields.Str()\n",
    "    timestamp = fields.DateTime()\n",
    "    order = fields.Str()\n",
    "    price = fields.Decimal(as_string=True)\n",
    "    commission = fields.Decimal(as_string=True)\n",
    "    volume = fields.Integer()\n",
    "    \n",
    "    @post_load\n",
    "    def make_trade(self, data):\n",
    "        return Trade(**data)\n",
    "    \n",
    "class ActivitySchema(Schema):\n",
    "    trades = fields.Nested(TradeSchema, many=True)\n",
    "    quotes = fields.Nested(StockSchema, many=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see this is a whole lot easier than doing it by hand using the standard library."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
